package com.tencent.qf.common.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.tencent.qf.common.config.SignatureConfig;
import com.tencent.qf.common.exception.ErrorCode;
import com.tencent.qf.common.pojo.HttpResult;
import com.tencent.qf.common.util.SignatureUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Aspect
@Component
public class SignatureAspect {

    @Autowired
    private SignatureConfig signatureConfig;

    @Pointcut("@annotation(com.tencent.qf.common.aspect.annotation.Signature)")
    public void pointCut() {
    }

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        String host = request.getHeader("host");
        String requestURI = request.getRequestURI();
        if("openapi-dev.local.e.qq.com".equals(host)
                || "openapi-test.local.e.qq.com".equals(host)
                || "openapi.local.e.qq.com".equals(host)
                || (StringUtils.isNotEmpty(requestURI) && requestURI.contains("apigw"))
                || signatureConfig.getDisable()){
            //内网访问才需要签名验证，网关访问不需要(网关的签名验证在网关做了)
            return joinPoint.proceed();
        }

        String appKey = request.getHeader("appKey");
        String time = request.getHeader("time");
        String sign = request.getHeader("sign");

        HttpResult errorRes = this.checkParam(appKey, time, sign);
        if(errorRes != null){
            return errorRes;
        }

        String appSecret = this.getAppSecret(appKey);
        if(StringUtils.isEmpty(appSecret)){
            log.info("参数【appKey】不正确");
            return HttpResult.error(ErrorCode.BAD_REQUEST.getCode(), "参数【appKey】不正确");
        }

        Object requestBody = this.buildRequestBody(joinPoint);
        HashMap<String, Object> map = JSON.parseObject(JSON.toJSONString(requestBody), new TypeReference<HashMap<String, Object>>() {
        });

        map.put("appKey", appKey);
        map.put("time", time);
        String signature = SignatureUtils.sign(map, appSecret);
        if(!sign.equals(signature)){
            log.info("签名校验失败,入参sign={} 签名sign={}", sign, signature);
            return HttpResult.error(ErrorCode.SIGN_FAILURE);
        }
        return joinPoint.proceed();
    }

    private String getAppSecret(String appKey) {
        Map<String, SignatureConfig.SignatureSecretInfo> signatureConf = signatureConfig.getMap();
        for(Map.Entry<String, SignatureConfig.SignatureSecretInfo> entry : signatureConf.entrySet()){
            SignatureConfig.SignatureSecretInfo value = entry.getValue();
            if(value.getAppKey().equals(appKey)){
                return value.getAppSecret();
            }
        }
        return null;
    }

    private HttpResult checkParam(String appKey, String time, String sign) {
        if(StringUtils.isEmpty(appKey)){
            log.info("参数【appKey】不能为空");
            return HttpResult.error(ErrorCode.BAD_REQUEST.getCode(), "参数【appKey】不能为空");
        }
        if(StringUtils.isEmpty(time)){
            log.info("参数【time】不能为空");
            return HttpResult.error(ErrorCode.BAD_REQUEST.getCode(), "参数【time】不能为空");
        }
        if(StringUtils.isEmpty(sign)){
            log.info("参数【sign】不能为空");
            return HttpResult.error(ErrorCode.BAD_REQUEST.getCode(), "参数【sign】不能为空");
        }
        return null;
    }

    private Object buildRequestBody(ProceedingJoinPoint joinPoint) {
        Object[] args = joinPoint.getArgs();
        Object requestBody = new Object();
        if(args != null && args.length > 0){
            for(Object arg : args){
                if(arg instanceof HttpServletRequest){
                    continue;
                }
                if(arg instanceof HttpServletResponse){
                    continue;
                }
                if(arg instanceof MultipartFile){
                    continue;
                }
                requestBody = arg;
            }
        }
        return requestBody;
    }
}
